package com.checksound;

import java.text.DecimalFormat;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.view.View;

  /**
   * Shows the results of the frequency calculation as a "note line", with the user's position indicated with a marker. 
   */

public class UiView extends View {

	//status none: no sound marker should be displayed
	public static final int SOUND_STATUS_NONE = 0;
	
	//status invalid: the "sound is invalid" marker should be displayed: we still want to see the marker because it is interesting
	//to know what the last valid sound was
	public static final int SOUND_STATUS_INVALID = 1;
	
	//status valid: show the "sound is valid" marker
	public static final int SOUND_STATUS_VALID = 2;
	
	//the paint object used by the canvas
	private Paint paint;
	
	// Get the screen's density scale
	final float scale = getResources().getDisplayMetrics().density;
	
	//width of the line
	private float lineWidth = (12.0f * scale + 0.5f);
	
	//width of the tone marker
	private float toneHeight = (100.0f * scale + 0.5f);
	
	//width of the tone marker
	private float toneWidth = (10.0f * scale + 0.5f);
	
	//width of the semitone marker
	private float semitoneHeight = (50.0f * scale + 0.5f);
	
	//width of the semitone marker
	private float semitoneWidth = (5.0f * scale + 0.5f);

	//number of semitones to show on the screen: must be a multiple of 2
	private int numberOfSemitonesToShowOnScreen = 4;
	
	//height of canvas
	float canvasHeight;
	
	//width of canvas
	float canvasWidth;

	//y is the vertical position of the centre of the line
	float y;

	//spacing of semitones
	float semitoneSpacing;
	
	//names of the notes
    String[] namesOfNotes = {"A", "B♭", "B", "C", "C♯", "D", "E♭", "E", "F", "F♯", "G", "G♯"};

    //the current frequency
    private float frequency;
    
    //frequency of the nearest discrete semitone
    private float nearestNoteFrequency;
    
    //index of the nearest note: 0=A, 1=Bflat, ...
    private int nearestNoteIndex;
    
    //distance of the nearest note from the frequency, calculated as a fraction of a semitone
    private float nearestNoteOffset;
    
    //note index, used in the calculation of the position of the note on the screen
	int noteIndex;
	
	//name of the note, used in the generation of the display
	String noteName;
	
	//position of the note along the line, used in the generation of the display
	float notePosition;
	
	//position of the first note we are going to display along the line
	int initialNoteIndex;
    
	//whether or not the view should be displayed
    boolean isActive = false;
    
    //status of the sound detection: three possible values:
    //0 = SOUND_STATUS_NONE = no sound has yet been detected
    //1 = SOUND_STATUS_INVALID = calculation of frequency was not possible from the sound
    //2 = SOUND_STATUS_VALID = a valid sound has been detected
    int soundStatus = 0;

    /**
     * Constructor.
     */    
    
	public UiView(Context context, AttributeSet attrs) {
		
		super(context, attrs);

		paint = new Paint();
	}
	
	  /**
	    * 
	    * Set the status of the sound: "valid", "invalid" or "none".
	    * 
	    * This will affect whether or not we show a marker and the style of the marker
	    * 
	    * @return integer soundStatus The status
	    */
	
	public int getSoundStatus()
	{
		return this.soundStatus;
	}
	
	  /**
	    * 
	    * Get the status of the sound: "valid", "invalid" or "none".
	    * 
	    * This affects whether or not we show a marker and the style of the marker
	    * 
	    * @return void
	    */
	
	public void setSoundStatus(int soundStatus)
	{
		this.soundStatus = soundStatus;
	}
	
	  /**
	    * 
	    * Update the parameters that control the display.
	    * 
	    * @param float frequency The frequency of the sound we are displaying
	    * @param float nearestNoteFrequency The frequency of the nearest note to the frequency
	    * @param integer nearestNoteIndex The index of the nearest note: 0=A, 1=B♭, 2=B etc
	    * @param float nearestNoteOffset The distance of the frequency to the nearest note as a fraction of a semitone
	    * @param boolean soundStatus The status of the sound: "valid", "invalid" or "none"
	    * 
	    */
	
	public void updateParams(float frequency, float nearestNoteFrequency, int nearestNoteIndex, float nearestNoteOffset, int soundStatus)
	{
		this.isActive = true;
		this.frequency = frequency;
		this.nearestNoteFrequency = nearestNoteFrequency;
		this.nearestNoteIndex = nearestNoteIndex;
		this.nearestNoteOffset = nearestNoteOffset;
		this.soundStatus = soundStatus;
	}
	
    /**
     * Draw the UI with the latest updated information.
     */
	
	@Override
	protected void onDraw(Canvas canvas) {
		
		if (!isActive)
		{
			return;
		}

		//Log.v("soundlog", "called onDraw");
		
		if (numberOfSemitonesToShowOnScreen % 2 != 0)
		{
			throw new RuntimeException("numberOfSemitonesToShowOnScreen must be an even number");
		}
		if (nearestNoteOffset <= -1.0 || nearestNoteOffset >= 1.0)
		{
			throw new RuntimeException("nearestNoteOffset must be a number between -1.0 and 1.0");
		}
		if (nearestNoteIndex < 0 || nearestNoteIndex > 11)
		{
			throw new RuntimeException("nearestNoteIndex must be a number between 0 and 11");
		}
		
		//height of canvas
		canvasHeight = getHeight();
		
		//width of canvas
		canvasWidth = getWidth();		

		//y is the vertical position of the centre of the line
		y = ((canvasHeight * 3.0f/ 5.0f));

		//spacing of semitones
		semitoneSpacing = canvasWidth / numberOfSemitonesToShowOnScreen;
		
		//display the center marker
		if (soundStatus == UiView.SOUND_STATUS_VALID)
		{
		  //if a sound was detected successfully, set the color of the central marker to green
		  paint.setColor(Color.parseColor("#59E817"));
		  //draw a big, vertical line to mark the position of the note on the line
	      canvas.drawRect( 
	    		  canvasWidth/2 - semitoneWidth/2, y - toneHeight * 0.7f,
	    		  canvasWidth/2 + semitoneWidth/ 2,  y + toneHeight, paint);
	     
		} else if (soundStatus == UiView.SOUND_STATUS_INVALID)
		{
	      //if a valid sound was not detected (usually because the sound was too quiet or had too much noise), set the color of the central marker to red
		  paint.setColor(Color.RED);
		  //draw a big, vertical line to mark the position of the note on the line
		  canvas.drawRect(
				  canvasWidth/2 - semitoneWidth/ 2, y - toneHeight * 0.7f,
				  canvasWidth/2 + semitoneWidth/ 2,  y + toneHeight, paint);
		  
		} else
		{
			//don't show any marker
		}

		//set the color of the line and markers
		paint.setColor(Color.BLACK);

		//set the alignment of the text
		paint.setTextAlign(Align.RIGHT);
		//set the size of the text
		paint.setTextSize((20.0f * scale + 0.5f));
		
		//print out frequency and frequency of the nearest note in the top right-hand corner
		if (soundStatus != UiView.SOUND_STATUS_NONE)
		{
			DecimalFormat df = new DecimalFormat("##.0");
			canvas.drawText(df.format(frequency) + " Hz", (canvasWidth - (8.0f * scale + 0.5f)), (20.0f * scale + 0.5f), paint);
			canvas.drawText(namesOfNotes[nearestNoteIndex] + ": " + df.format(nearestNoteFrequency) + " Hz", (canvasWidth - (8.0f * scale + 0.5f)), (40.0f * scale + 0.5f), paint);
		}
		
		//set the alignment of the text
		paint.setTextAlign(Align.CENTER);
		//set the size of the text
		paint.setTextSize((30.0f * scale + 0.5f));
		
		//draw the main line as a horizontal rectangle
		canvas.drawRect(0.0f, y - lineWidth/ 2,
				canvasWidth, y + lineWidth/ 2, paint);

		//calculate WHICH note this is
        //indexes: 0 == A, 1 == B♭, 2 == B, ...
		initialNoteIndex = nearestNoteIndex - (numberOfSemitonesToShowOnScreen/2);
		//position of note on the screen
		for (int i = 0; i < (numberOfSemitonesToShowOnScreen + 1); i++)
		{
			if ((initialNoteIndex + i) >= 0)
			{
				noteIndex = (initialNoteIndex + i) % 12;
			} else
			{
				if (((initialNoteIndex + i) % 12) == 0)
				{
					noteIndex = 0;
				} else
				{
					noteIndex = 12 + ((initialNoteIndex + i) % 12);
				}
			}
			notePosition =  ((float) i - nearestNoteOffset) * semitoneSpacing;
			//add markers for the notes as vertical rectangles
			
			noteName = namesOfNotes[noteIndex];

			//if it's a flat or sharp note, use a smaller rectangle
			if (noteIndex == 1 || noteIndex == 4 || noteIndex == 6 || noteIndex == 9 || noteIndex == 11)
			{
				canvas.drawRect(
						notePosition - semitoneWidth/ 2, y - semitoneHeight /2,
						notePosition + semitoneWidth/ 2, y + semitoneHeight /2,  paint);
				canvas.drawText(noteName, notePosition, (y - 1.2f * semitoneHeight/ 2), paint);
			} else
			{
			  canvas.drawRect(
					  notePosition - toneWidth/ 2, y - toneHeight /2,
					  notePosition + toneWidth/ 2, y + toneHeight /2, paint);
			  canvas.drawText(noteName, notePosition, (y - 1.2f * toneHeight/ 2), paint);
			}
		}

	}

}
